#include <stdio.h>
#include <assert.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#define MAX 1024
#define ARGC 64
#define SEP " "

enum redir
{
    REDIR_INPUT = 0,
    REDIR_OUTPUT,
    REDIR_APPEND,
    REDIR_NONE
};

typedef enum redir redir;

char* checkDir(char commandstr[], redir* redir_type)
{
    char* filename = NULL;
    while(*commandstr != '\0')
    {
        if (*commandstr == '>')
        {
            *commandstr++ = '\0';
            *redir_type = REDIR_OUTPUT;
            if (*commandstr == '>')
            {
                *commandstr++ = '\0';
                *redir_type = REDIR_APPEND;
            }
            while (isspace(*commandstr))
            {
                commandstr++;
            }
            filename = commandstr;
            while (!isspace(*commandstr) && *commandstr != '\0')
            {
                commandstr++;
            }
            *commandstr = '\0';
            return filename;
        }
        else 
        {
            commandstr++;
        }
    }
    return NULL;
}

int split(char* commandstr, char* argv[])
{
    assert(commandstr);
    assert(argv);
    argv[0] = strtok(commandstr, SEP);
    if (argv[0] == NULL) return -1;
    int i = 1;
    while ((argv[i++] = strtok(NULL, SEP)));
    //while (1)
    //{
    //    argv[i] = strtok(NULL, SEP);
    //    if (argv[i] == NULL) break;
    //    ++i;
    //}
    return 0;
}

void debugPrint(char* argv[])
{
    int i = 0;
    for (; argv[i]; ++i)
    {
        printf("%d:%s\n", i, argv[i]);
    }
}

void showEnv()
{
    extern char** environ;
    int i = 0;
    for(; environ[i]; ++i) printf("%d:%s\n", i, environ[i]);
}

int main()
{
    int last_exit = 0;
    char myenv[32][256];
    int env_index = 0;
    while (1)
    {
        redir redir_type = REDIR_NONE;
        char commandstr[MAX] = {0};
        char* argv[ARGC] = {NULL};
        printf("[lgr@mymachine currpath]# ");
        fflush(stdout);
        char* s = fgets(commandstr, sizeof(commandstr), stdin);
        assert(s);
        (void)s;
        commandstr[strlen(commandstr)-1] = '\0';

        char* filename = checkDir(commandstr, &redir_type);

        int n = split(commandstr, argv);
        if (n != 0) continue;
        // cd .. /  内建命令
        if (strcmp(argv[0], "cd") == 0)
        {
            if (argv[1] != NULL) chdir(argv[1]);
            continue;
        }
        else if (strcmp(argv[0], "export") == 0)
        {
            if (argv[1] != NULL) 
            {
                strcpy(myenv[env_index], argv[1]);
                putenv(myenv[env_index++]);
            }
            continue;
        }
        else if (strcmp(argv[0], "env") == 0)
        {
            showEnv();
            continue;
        }
        else if (strcmp(argv[0], "echo") == 0)
        {
            const char* target_env = NULL;
            if (*argv[1] == '$') 
            {
                if (argv[1][1] == '?')
                {
                    printf("%d\n", last_exit);
                    continue;
                }
                else target_env = getenv(argv[1]+1);
            }
            if (target_env != NULL) printf("%s=%s\n", argv[1]+1, target_env);
            continue;
        }
        // version2
        if (strcmp(argv[0], "ls") == 0)
        {
            int pos = 0;
            while (argv[pos]) ++pos;
            argv[pos++] = (char*)"--color=auto";
            argv[pos] = NULL;
        }
        //printf("%s\n", commandstr);
        //debugPrint(argv); 
        
        //version1
        pid_t id = fork();
        assert(id >= 0);
        (void)id;
        if (id == 0)
        {
            //child
            if (redir_type != REDIR_NONE)
            {
                int fd = 0;
                if (redir_type == REDIR_APPEND)
                {
                    fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, 0666);
                }
                else
                {
                    fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
                }
                dup2(fd, 1);
            }
            execvp(argv[0], argv);
            exit(1);
        }
        int status = 0;
        pid_t ret = waitpid(id, &status, 0);
        if (ret > 0)
        {
            last_exit = WEXITSTATUS(status);
        }
    }
    return 0;
}
